package cloudbus

import (
	"encoding/json"
	"regexp"
	"strings"
	"sync"
	"sync/atomic"
	"zstackgo/pkg/core/db"
	"zstackgo/pkg/core/webhook"
	"zstackgo/pkg/header"
	"zstackgo/pkg/header/apimediator"
	"zstackgo/pkg/header/message"
	"zstackgo/spring"

	"github.com/meilihao/golib/v2/log"
	"go.uber.org/zap"
)

var (
	_ EventFacade                             = NewEventFacadeImpl()
	_ CloudBusEventListener                   = NewEventFacadeImpl()
	_ header.Component                        = NewEventFacadeImpl()
	_ apimediator.GlobalApiMessageInterceptor = NewEventFacadeImpl()
)

func init() {
	o := new(EventFacadeImpl)
	spring.Register(o, "EventFacade", []*spring.PluginExtension{
		{
			Interface: "header.Component",
		},
		{
			Interface: "header.apimediator.GlobalApiMessageInterceptor",
		},
	})
}

// 先注册一个路径用于接收事件，然后沿着该路径发送一个事件，该事件注册的函数则会被调用 by EventFacadeImpl
type EventFacadeImpl struct {
	bus          CloudBus
	global       map[string]*callbackWrapper
	local        map[string]*callbackWrapper
	unsubscriber EventSubscriberReceipt
	lock         sync.RWMutex
}

func NewEventFacadeImpl() *EventFacadeImpl {
	return &EventFacadeImpl{}
}

func (impl *EventFacadeImpl) GetMessageClassToIntercept() []any {
	return []any{}
}

func (impl *EventFacadeImpl) GetPosition() apimediator.InterceptorPosition {
	return apimediator.InterceptorPositionFront
}

func (impl *EventFacadeImpl) Intercept(msg message.APIMessage) (message.APIMessage, error) {
	// todo delete

	return msg, nil
}

type callbackWrapper struct {
	path     string
	glob     *regexp.Regexp
	callback AbstractEventFacadeCallback
	hasRun   atomic.Bool
	p        *EventFacadeImpl
}

var (
	pathReg = regexp.MustCompile(`"\{.*\}"`)
)

func newCallbackWrapper(p *EventFacadeImpl, path string, callback AbstractEventFacadeCallback) *callbackWrapper {
	w := &callbackWrapper{
		path:     path,
		glob:     createRegexFromGlob(pathReg.ReplaceAllString(path, ".*")),
		callback: callback,
		p:        p,
	}
	if callback.GetClass() == "AutoOffEventCallback" {
		w.hasRun.Store(false)
	}
	return w
}

func (w *callbackWrapper) call(e *CanonicalEvent) {
	//   TaskContext.removeTaskContext(); // todo
	if v, ok := w.callback.(EventRunnable); ok {
		v.Run()
	} else {
		tokens := w.p.tokenize(e.path, w.path)
		tokens[META_DATA_MANAGEMENT_NODE_ID] = e.GetManagementNodeId()
		tokens[META_DATA_PATH] = e.path
		var data any
		if e.content != nil {
			data = e.content
		}

		if v, ok := w.callback.(EventCallback); ok {
			v.Run(tokens, data)
		} else if v2, ok := w.callback.(AutoOffEventCallback); ok {
			if !w.hasRun.CompareAndSwap(false, true) {
				// the callback is being called
				return
			}

			if v2.Run(tokens, data) {
				w.p.Off(v2)
			} else {
				w.hasRun.Store(false)
			}
		}

	}
}

func createRegexFromGlob(glob string) *regexp.Regexp {
	out := "^"
	for _, v := range []byte(glob) {
		switch v {
		case '*':
			out += ".*"
			break
		case '?':
			out += "."
			break
		case '\\':
			out += `\\`
			break
		default:
			out += string(v)
		}
	}
	out += "$"
	return regexp.MustCompile(out)
}

func (impl *EventFacadeImpl) tokenize(str1, str2 string) map[string]string {
	token := strings.Split(str1, "/")
	var origins []string
	if len(token) > 0 {
		origins = append(origins, token...)
	}

	token = strings.Split(str2, "/")
	var t []string
	if len(token) > 0 {
		t = append(t, token...)
	}

	ret := make(map[string]string)
	for i, v := range t {
		if !strings.HasPrefix(v, "{") || !strings.HasSuffix(v, "}") {
			continue
		}

		v = strings.ReplaceAll(v, "{", "")
		v = strings.ReplaceAll(v, "}", "")

		ret[v] = origins[i]
	}

	return ret
}

func (impl *EventFacadeImpl) OnAutoOffEventCallback(path string, cb AutoOffEventCallback) {
	impl.lock.Lock()
	defer impl.lock.Unlock()

	impl.global[cb.UniqueIdentity()] = newCallbackWrapper(impl, path, cb)
}

// 区分on和onLocal: 一个是通过消息总线触发，一个是在当前JVM进程内触发——这意味着一个支持ManagerNode集群事件，一个只支持单个MN事件.这是来自于ZStack的业务场景——有些事情需要MN一起做，有些事情一个MN做了其他MN就不用做了
func (impl *EventFacadeImpl) OnEventCallback(path string, cb EventCallback) {
	impl.lock.Lock()
	defer impl.lock.Unlock()

	impl.global[cb.UniqueIdentity()] = newCallbackWrapper(impl, path, cb)
}

func (impl *EventFacadeImpl) OnEventRunnable(path string, cb EventRunnable) {
	impl.lock.Lock()
	defer impl.lock.Unlock()

	impl.global[cb.UniqueIdentity()] = newCallbackWrapper(impl, path, cb)
}

func (impl *EventFacadeImpl) Off(cb AbstractEventFacadeCallback) {
	impl.lock.Lock()
	defer impl.lock.Unlock()

	delete(impl.global, cb.UniqueIdentity())
	delete(impl.local, cb.UniqueIdentity())
}

func (impl *EventFacadeImpl) OnLocalAutoOffEventCallback(path string, cb AutoOffEventCallback) {
	impl.lock.Lock()
	defer impl.lock.Unlock()

	impl.local[cb.UniqueIdentity()] = newCallbackWrapper(impl, path, cb)
}

func (impl *EventFacadeImpl) OnLocalEventCallback(path string, cb EventCallback) {
	impl.lock.Lock()
	defer impl.lock.Unlock()

	impl.local[cb.UniqueIdentity()] = newCallbackWrapper(impl, path, cb)
}

func (impl *EventFacadeImpl) OnLocalEventRunnable(path string, cb EventRunnable) {
	impl.lock.Lock()
	defer impl.lock.Unlock()

	impl.local[cb.UniqueIdentity()] = newCallbackWrapper(impl, path, cb)
}

func (impl *EventFacadeImpl) Fire(path string, data any) {
	if path == "" {
		panic("missing path")
	}

	evt := &CanonicalEvent{
		path:             path,
		managementNodeId: PlatformGetManagementServerId(),
	}
	if data != nil {
		evt.content = data
	}
	impl.fireLocal(evt)    // call local handle event // 从local这个map中找到对应的event并调用
	impl.callWebhooks(evt) // send to webhook
	impl.bus.Publish(evt)
}

// WebHook是ZStack向前端主动通信的手段之一。在注册了相应EventPath后，该path被调用后则会向相应的URL发送content
func (impl *EventFacadeImpl) callWebhooks(event *CanonicalEvent) {
	var ls []webhook.WebhookVO

	w := webhook.NewWebhookCaller(func() {
		vos := make([]webhook.WebhookVO, 0)
		err := db.Dbf.GetEntityManager().Model(webhook.ModelWebhookVO).Where("Type = ?", WEBHOOK_TYPE).Find(vos).Error
		if err != nil {
			log.Glog.Error("callWebhooks", zap.Error(err))
			return
		}

		for _, vo := range vos {
			if createRegexFromGlob(pathReg.ReplaceAllString(vo.Opaque, ".*")).MatchString(event.path) {
				ls = append(ls, vo)
			}
		}
	})
	w.Call()

	if len(ls) > 0 {
		body, _ := json.Marshal(event)
		w.PostToWebhooks(ls, body)
	}
}

func (impl *EventFacadeImpl) fireLocal(cevt *CanonicalEvent) {
	var wrappers map[string]*callbackWrapper
	impl.lock.RLock()
	wrappers = make(map[string]*callbackWrapper, len(impl.global))
	for k, v := range impl.local {
		wrappers[k] = v
	}
	impl.lock.RUnlock()

	for _, w := range wrappers {
		if w.glob.MatchString(cevt.path) {
			w.call(cevt)
		}
	}
}

func (impl *EventFacadeImpl) IsFromThisManagementNode(tokens map[string]string) bool {
	return PlatformGetManagementServerId() == tokens[META_DATA_MANAGEMENT_NODE_ID]
}

func (impl *EventFacadeImpl) HandleEvent(evt message.Event) bool {
	cevt, ok := evt.(*CanonicalEvent)
	if !ok {
		return false
	}

	var wrappers map[string]*callbackWrapper
	impl.lock.RLock()
	wrappers = make(map[string]*callbackWrapper, len(impl.global))
	for k, v := range impl.global {
		wrappers[k] = v
	}
	impl.lock.RUnlock()

	for _, w := range wrappers {
		if w.glob.MatchString(cevt.path) {
			w.call(cevt)
		}
	}

	return false
}

func (impl *EventFacadeImpl) Start() error {
	impl.unsubscriber = impl.bus.SubscribeEvent(impl, &CanonicalEvent{})
	return nil
}

func (impl *EventFacadeImpl) Stop() error {
	if impl.unsubscriber != nil {
		impl.unsubscriber.UnsubscribeAll()
	}

	return nil
}
